#include <wchar.h>
#include <iostream>
#include <stdio.h>
#include <sstream>
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>
#include "SubtitlePlayer.h"
#include "Constants.h"
#include "Utilities.h"
using Utilities::WstringToNumber;
using std::cout;
using std::wcout;
using std::endl;
using boost::algorithm::trim;
using std::wistringstream;

namespace SubtitlePlayerSpace
{
    //static variable members
    const wstring SubtitlePlayer::supportedEncodings[] = {L"UTF-8", L"UTF-16LE", L"UNICODE"};
    const int SubtitlePlayer::supportedEncodingsCount = 3;
    const std::wregex SubtitlePlayer::timeRegex(L"^[[:digit:]]\\{2\\}[[:digit:]]*:[[:digit:]]\\{2,2\\}:[[:digit:]]\\{2,2\\},[[:digit:]]\\{3,3\\} "
                L"--> [[:digit:]]\\{2,2\\}:[[:digit:]]\\{2,2\\}:[[:digit:]]\\{2,2\\},[[:digit:]]\\{3,3\\}$", std::regex_constants::grep);
    wstring SubtitlePlayer::errorMessage = L"Everything is ok";

    //methods

    SubtitlePlayer::SubtitlePlayer()
    {
        subLines.insert(std::pair<long, wstring>(0, L"Not initialized"));
        nextSub = subLines.begin();
    }

    SubtitlePlayer::SubtitlePlayer(const LPCWSTR path, const LPCWSTR encoding, int& errorStatus):
    filePath(path),
    subFile(NULL)
    {
        errorStatus = SetEncoding(encoding);
        if (errorStatus != 0) {
            return;
        }
        errorStatus = OpenSubtitles(path);
        if (errorStatus != 0) {
            return;
        }
        //PrintMap();
    }

    SubtitlePlayer::~SubtitlePlayer()
    {
        if (subFile != NULL) {
            fclose(subFile);  //:TODO len ak je nejaky otvoreny
        }
    }

    boolean SubtitlePlayer::IsEncodingAccepted(wstring encoding) const
    {
        for (int i = 0; i < supportedEncodingsCount; i++) {
            if (encoding.compare(supportedEncodings[i]) == 0){
                return true;
            }
        }
        return false;
    }

    LPCWSTR SubtitlePlayer::GetLastErrorMessage()
    {
        return errorMessage.c_str();
    }

    int SubtitlePlayer::ReadFileLines(int& processedLines)
    {
        wstring buffer;
        int fileSize = GetSizeOfFile(filePath);
        processedLines = 0;

        buffer.resize(fileSize);
        SubtitlePlayer::RowType rt = SubtitlePlayer::RowType::UnknownRow;
        long startTime = 0;
        long endTime = 0;
        wstring currentSub = L"";
        while (fgetws(&buffer[0], fileSize, subFile) != NULL) {
            trim(buffer);
            buffer.erase(std::remove(buffer.begin(), buffer.end(), '\n'), buffer.end());
            buffer.resize(wcslen(&buffer[0]));
            rt = RowType::TextRow;
            rt = GetRowType(buffer);
            switch (rt) {
            case UnknownRow:
                return 0;
                break;
            case  OrderRow:
                break;
            case TextRow:
                if (!currentSub.empty()) {
                    currentSub += L"\n";
                }
                currentSub += buffer;
                break;
            case TimeRow:
                if (ConvertTimeRowToMS(buffer, startTime, endTime) != 0) {
                    return 2;
                }
                if (startTime > endTime) {
                    return 3;
                }
            case EmptyRow:
                if (!currentSub.empty()) {
                    InsertSubLine(startTime, currentSub);
                    InsertSubLine(endTime, L" ");
                    currentSub = L"";
                }
                break;
            default:
                return 1;
            }
            //fileLines.insert( .push_back(buffer);
            buffer.resize(fileSize);
            processedLines++;
        }
        return 0;
    }



    void SubtitlePlayer::InsertSubLine(long time, const wstring & currentSub)
    {
        map<long, wstring>::iterator it = subLines.find(time);
        if (it != subLines.end()) {
            subLines.erase(it);
        }
        subLines.insert(std::pair<long, wstring>(time, currentSub));
    }

    void SubtitlePlayer::PrintMap()
    {
        for (std::map<long, wstring>::iterator it = subLines.begin(); it != subLines.end(); it++) {
            wcout<<it->first<<" - "<<it->second<<endl;
        }
    }

    int SubtitlePlayer::ConvertTimeRowToMS(const wstring& timeRow, long& startTime, long& endTime)
    {
        int result = GetMSFromString(timeRow.substr(0, 12), startTime);
        if (result != 0) {
            return result;
        }
        result = GetMSFromString(timeRow.substr(17, 29), endTime);
        if (result != 0) {
            return result;
        }
        return 0;
    }

    int SubtitlePlayer::GetMSFromString(const wstring& timeString, long& time)
    {
        long miliseconds = 0;
        long transformedLong = 0;
        if (WstringToNumber(timeString.substr(0,2), transformedLong) != 0) {
            return 1;
        }
        miliseconds = transformedLong * 3600000;
        if (WstringToNumber(timeString.substr(3,4), transformedLong) != 0) {
            return 2;
        }
        miliseconds += transformedLong * 60000;
        if (WstringToNumber(timeString.substr(6,7), transformedLong) != 0) {
            return 3;
        }
        miliseconds += transformedLong * 1000;
        if (WstringToNumber(timeString.substr(9,11), transformedLong) != 0) {
            return 3;
        }
        miliseconds += transformedLong;
        time = miliseconds;
        return 0;
    }

    SubtitlePlayer::RowType SubtitlePlayer::GetRowType(const wstring& row, RowType previous)
    {
        static RowType previousRow = EmptyRow;
        RowType currentRow;
        if (previous != UnknownRow) {
            previousRow = previous;
        }
        static int previousNumber = 0;
        switch (previousRow) {
        case EmptyRow:
            if (row.length() == 0) {
                currentRow = EmptyRow;
            }
            else {
                int currentNumber;
                if (WstringToNumber(row, currentNumber) != 0) {
                    currentRow = UnknownRow;
                }
                if (currentNumber != (previousNumber + 1)) {
                    currentRow = UnknownRow;
                }
                previousNumber = currentNumber;
                currentRow = OrderRow;
            }
            break;
        case OrderRow:
            if (std::regex_match(row, timeRegex)) {
                currentRow = TimeRow;
            }
            else {
                currentRow = UnknownRow;
            }
            break;
        case TimeRow:
            if (row.length() == 0) {
                currentRow = UnknownRow;
            }
            else {
                currentRow = TextRow;
            }
            break;
        case TextRow:
            if (row.length() == 0) {
                currentRow = EmptyRow;
            }
            else {
                currentRow = TextRow;
            }
            break;
        case UnknownRow:
            break;
        default:
            currentRow = UnknownRow;
        }
        previousRow = currentRow;
        return currentRow;
    }

    size_t GetSizeOfFile(wstring filePath)
    {
        struct _stat fileinfo;
        if (_wstat(filePath.c_str(), &fileinfo) == 0) {
            return fileinfo.st_size;
        }
        else {
            return -1;
        }
        return -1;
    }

    //int SubtitlePlayer::GetSubNumber(vector<wstring>::iterator linesIt, vector<wstring>::const_iterator endIt, int &lastLine, vector<wstring>::iterator &outIt) const
    //{
    //    lastLine += SkipEmpty(linesIt, endIt, linesIt);
    //    //I will incremet lastLine one more, becouse I will work on next line of document.
    //    lastLine++;
    //    int extractedNumber;
    //    if ((linesIt == endIt) || (wistringstream (*linesIt) >> extractedNumber).fail()) {
    //        return 0;
    //    }
    //    else {
    //        linesIt++;
    //        outIt = linesIt;
    //        return extractedNumber;
    //    }
    //}

    int SubtitlePlayer::SkipEmpty(vector<wstring>::iterator linesIt, vector<wstring>::const_iterator endIt, vector<wstring>::iterator &outIt) const
    {
        int skipped = 0;
        while ((linesIt != endIt) && ((*linesIt).length() ==  0)) {
            linesIt++;
            skipped++;
        }
        outIt = linesIt;
        return skipped;
    }

    int SubtitlePlayer::OpenSubtitles(LPCWSTR path)
    {
        wstring mode = wstring(L"r, ccs=") + encoding;
        if (_wfopen_s(&subFile, filePath.c_str(), mode.c_str()) != 0 || subFile == NULL) {
            errorMessage = L"Can\'t open subtitle file.";
            return 2;
        }

        if (GetSizeOfFile(filePath) <= 0) {
            errorMessage = L"Can't get size of subtitle file, or file is empty";
            return 3;
        }

        int readLines;
        int transformResult = ReadFileLines(readLines);
        if (transformResult != 0) {
            errorMessage = L"Error in subtitle file on line ";
            errorMessage += boost::lexical_cast<wstring>(readLines);
            return 4;
        }
        return 0;
    }

    int SubtitlePlayer::SetEncoding(LPCWSTR encoding)
    {
        if (!IsEncodingAccepted(encoding)) {
            errorMessage = Constants::ERR_UNSUPPORTED_ENCODING_DESC;
            return Constants::ERR_UNSUPPORTED_ENCODING;
        }
        this->encoding = encoding;
        return 0;
    }

    long SubtitlePlayer::GetEndTime()
    {
        long value = (--subLines.end())->first;
        return (--subLines.end())->first;
    }

    int SubtitlePlayer::GetFirstSubSize()
    {
        return subLines.begin()->second.size();
    }

    wstring SubtitlePlayer::GetFirstSub(long& endTime)
    {
        nextSub = subLines.begin();
        if (++nextSub == subLines.end()) {
            endTime = -1;
        }
        else {
            endTime = nextSub->first;
        }
        return subLines.begin()->second;
    }

    wstring SubtitlePlayer::GetNextSub(long& duration)
    {
        if (nextSub == subLines.end()) {
            duration = -1;
            return L"End of subtitles";
        }
        else {
            long thisTime = nextSub->first;
            wstring thisText = nextSub->second;
            if (++nextSub == subLines.end()) {
                duration = -1;
            }
            else {
                duration = nextSub->first - thisTime;
            }
            return thisText;
        }
    }
}